﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace gamecontrol
{
    public delegate void GameOverDelegate();
    public delegate void HitScoresDelegate();
    
    public class PhysicalBody
    {
        FrameworkElement body;
        private double x;
        private double y;

        private double velocityX; // for internal use
        private double velocityY;

        private double velocity; //user perceived the body velocity

        //runtime environment characteristics:
        private ScreenRefreshRate refreshRate;
        private Size extends;
        private  GameOverDelegate gameOver;
        private  HitScoresDelegate hitScores;

        public PhysicalBody(FrameworkElement shape, ScreenRefreshRate aRefreshRate, 
                                double velocity, GameOverDelegate gameOver, HitScoresDelegate hitScores)
        {
            body = shape;
            refreshRate = aRefreshRate;
            this.velocity = velocity;
            this.gameOver = gameOver;
            this.hitScores = hitScores;
            velocityX = velocity;
            velocityY = velocity;
        }

        // called on changing the container geometry
        public void onSizeChanged(Size containerSize)
        {
            extends = containerSize;
            y = extends.Height / 2;
            x = extends.Width / 2;
        }

        //adjust desired object speed to current screen refresh rate
        private void updateVelocityByRefreshRate()
        {
            //TODO:
        }

        // calculates current position, called in screen refresh hook
        public void updatePosition()
        {
            if (!refreshRate.isValid())
                return;

            updateVelocityByRefreshRate();

            assessCollision();
            assessExtends();
            applyMovement();
        }

        private void assessExtends()
        {
            //TODO: re-workme to use assessCollision()
            if (x < 0)
                velocityX = velocity;
            if (x > extends.Width)
                velocityX = -velocity;

            if (y < 0)
                velocityY = velocity;
            if (y > extends.Height)
            {
                velocityY = -velocity;
                gameOver();
            }
        }

        private void assessCollision()
        {
            // get bounding box and offset the box in movement direction on one step
            Canvas canvas = (Canvas)body.Parent;
            var transform = canvas.TransformToVisual(Application.Current.RootVisual); // for converting screen-to-control coordinates

            var origin = transform.Transform(new Point(Canvas.GetLeft(body) + velocityX, Canvas.GetTop(body) + velocityY));

            var boundingBox = new Rect(origin, new Size(body.ActualWidth, body.ActualHeight));
            // check whether there are element exist on the body's way
            if (isCollided(VisualTreeHelper.FindElementsInHostCoordinates(new Point(boundingBox.Right, boundingBox.Bottom), (Canvas)body.Parent)) ||
                        isCollided(VisualTreeHelper.FindElementsInHostCoordinates(new Point(boundingBox.Right, boundingBox.Y), (Canvas)body.Parent)) ||
                            isCollided(VisualTreeHelper.FindElementsInHostCoordinates(new Point(boundingBox.X, boundingBox.Bottom), (Canvas)body.Parent)))
            {
                bounceBackY();
            }
        }

        private bool isCollided(IEnumerable<UIElement> enumerable)
        {
            //NOTE : there is no method to return the collection size in the assembly that is used by Silverlight thus, enumerating 
            foreach (UIElement e in enumerable)
            {
                // filter out alls except Shapes
                if (e.GetType() == typeof(Rectangle))
                {
                    var rect = e as Rectangle;
                    if (rect.Name == "bouncer")
                        hitScores();
                    else
                    {
                        var brick = ((Canvas)rect.Parent).Parent as Brick;
                        brick.onHit();
                    }
                    return true;
                }
            }
            return false;
        }

        private void applyMovement()
        {
            x += velocityX;
            y += velocityY;
            Canvas.SetLeft(body, x);
            Canvas.SetTop(body, y);
        }

        private void bounceBackXY()
        {
            bounceBackX();
            bounceBackY();
        }

        private void bounceBackX()
        {
            velocityX *= -1;
        }

        private void bounceBackY()
        {
            velocityY *= -1;
        }
    }
}
